package com.sixkey.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sixkey.constant.CommonConstant;
import com.sixkey.constant.RabbitMQConstant;
import com.sixkey.entity.*;
import com.sixkey.entity.vo.DeptUserVo;
import com.sixkey.entity.vo.ReservationVo;
import com.sixkey.entity.vo.RsAddLengthVo;
import com.sixkey.exception.ServiceException;
import com.sixkey.mapper.DepartmentMapper;
import com.sixkey.mapper.ReservationMapper;
import com.sixkey.mapper.RoomMapper;
import com.sixkey.rabbitmq.RabbitReceiver;
import com.sixkey.rabbitmq.RabbitService;
import com.sixkey.response.ResponseEnum;
import com.sixkey.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sixkey.utils.JodaTimeUtils;
import com.sixkey.utils.TimeStringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.LocalDateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author author
 * @since 2023-10-05
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ReservationServiceImpl extends ServiceImpl<ReservationMapper, Reservation> implements IReservationService {

    private final RoomMapper roomMapper;

    private final IAsyncService asyncService;

    private final IUserService userService;

    private final RabbitService rabbitService;

    private final DepartmentMapper departmentMapper;

    private final ISignService signService;

    private final IAuthorParticipantService authorParticipantService;

    /**
     * 小程序端保存立即预约会议数据
     * @param reservation
     * @return
     */
    @Override
    public void saveReservation(Reservation reservation) {
        //用户立即预约后，应将预约的会议室状态修改为预约中状态
        Room room = roomMapper.selectOne(new QueryWrapper<Room>().eq("name", reservation.getRoomName()));
        room.setStatus(CommonConstant.ROOM_STATUS_RESERVATION);
        //修改会议室状态
        roomMapper.updateById(room);
        //获取字符串预约时间
        List<String> createTime = reservation.getBeginEndTime();
        //会议开始时间
        String beginTime = createTime.get(0);
        //会议结束时间
        String endTime = createTime.get(1);
        //字符串转日期类
        LocalDateTime beginLocalDateTime = LocalDateTime.parse(beginTime, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm"));
        LocalDateTime endLocalDateTime = LocalDateTime.parse(endTime, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm"));
        //如果预约时间<当前时间，无法预约会议，因为时光无法倒退
        boolean before = JodaTimeUtils.isBefore(beginLocalDateTime, JodaTimeUtils.getCurrentDateTime());
        if(before){
            throw new ServiceException(ResponseEnum.RESERVATION_TIME);
        }
        //计算两个日期时间之间的小时数
        int hours = JodaTimeUtils.getHoursBetween(beginLocalDateTime, endLocalDateTime);
        if(hours > 24){
            throw new ServiceException(ResponseEnum.LENGTH_NOT_DAY);
        }
        //会议状态为未开始
        reservation.setStatus(CommonConstant.RESERVATION_STATUS_1);
        reservation.setBeginTime(TimeStringUtils.getDateTimeString(beginTime,endTime));
        //保存
        this.save(reservation);
        Reservation newReservation = this.getOne(new QueryWrapper<Reservation>().eq("theme", reservation.getTheme()));
        AuthorParticipant authorParticipant = new AuthorParticipant();
        authorParticipant.setReservationId(newReservation.getId());
        authorParticipant.setAuthorPhone(reservation.getAuthorPhone());
        authorParticipant.setParticipants(reservation.getParticipants());
        rabbitService.sendMessage(RabbitMQConstant.EXCHANGE_RESERVATION,RabbitMQConstant.ROUTING_RESERVATION,authorParticipant);
        log.info("异步发送会议通知消息");
        asyncService.sendSuccessMessage(reservation.getParticipants(),reservation.getAuthorPhone(),beginTime);
    }

    @Override
    public Page<Reservation> queryList(Integer pageNo, Integer pageSize, ReservationVo reservationVo) {
        Page<Reservation> pageInfo = new Page<>(pageNo, pageSize);
        LambdaQueryWrapper<Reservation> wrapper = new LambdaQueryWrapper<>();
        //添加过滤条件(模糊查询)
        wrapper.like(!StringUtils.isEmpty(reservationVo.getTheme()),Reservation::getTheme,reservationVo.getTheme());
        wrapper.like(!StringUtils.isEmpty(reservationVo.getStatus()),Reservation::getStatus,reservationVo.getStatus());
        //添加排序条件
        wrapper.orderByDesc(Reservation::getUpdateTime);
        Page<Reservation> reservationPage = this.page(pageInfo, wrapper);
        List<Reservation> records = reservationPage.getRecords();
        if(CollectionUtils.isEmpty(records)){
            return null;
        }
        for (int i = 0; i < records.size(); i++) {
            List<AuthorParticipant> authorParticipantList = authorParticipantService.list(new QueryWrapper<AuthorParticipant>().eq("reservation_id", records.get(i).getId()));
            records.get(i).setAuthorPhone(authorParticipantList.get(i).getAuthorPhone());
            List<String> participants = authorParticipantList.stream().map(AuthorParticipant::getParticipant).collect(Collectors.toList());
            records.get(i).setParticipants(participants);
        }
        return reservationPage;
    }

    /**
     * 批量或单个删除会议
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeBatch(List<Integer> id) {
        //
        List<Reservation> reservations = baseMapper.selectBatchIds(id);
        //可以删除未开始和已结束的会议
        List<Reservation> list = reservations.stream().filter(reservation -> reservation.getStatus().equalsIgnoreCase(CommonConstant.RESERVATION_STATUS_2)).collect(Collectors.toList());
        if(!CollectionUtils.isEmpty(list)){
            throw new ServiceException(ResponseEnum.MEET_NOT_DELETE);
        }
        //批量或单个删除会议
        //删除之前把关联数据也删除了
        id.stream().forEach(item -> {
            authorParticipantService.remove(new QueryWrapper<AuthorParticipant>().eq("reservation_id",item));
        });
        baseMapper.deleteBatchIds(id);
    }

    /**
     * 修改会议
     * @param reservation
     */
    @Override
    public void updateReservation(Reservation reservation) {
        this.updateById(reservation);
    }

    /**
     * 小程序端我的会议数据展示
     * @param authorPhone
     * @return
     */
    @Override
    public List<Reservation> getReservationList(String authorPhone) {
        QueryWrapper<AuthorParticipant> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("author_phone",authorPhone).or()
                .eq("participant",authorPhone);
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(queryWrapper);
        if(CollectionUtils.isEmpty(authorParticipantList)){
            return null;
        }
        List<Integer> reservationIds = authorParticipantList.stream().map(AuthorParticipant::getReservationId).collect(Collectors.toList());
        //把已结束的会议排除
        List<Reservation> reservations = this.listByIds(reservationIds);
        List<Reservation> reservationList = reservations.stream().filter(item -> (item.getStatus().equalsIgnoreCase(CommonConstant.RESERVATION_STATUS_1)) || (item.getStatus().equalsIgnoreCase(CommonConstant.RESERVATION_STATUS_2))).collect(Collectors.toList());
        //将会议开始和结束时间做一下处理
        reservationList.stream().forEach(item ->{
            item.setBeginTime(TimeStringUtils.getBeginEndTime(item.getBeginTime()));
            item.setAuthorPhone(authorParticipantList.get(0).getAuthorPhone());
        });
        return reservationList;
    }

    /**
     * 小程序端根据主题搜索会议
     * @param theme
     * @return
     */
    @Override
    public List<Reservation> getReservation(String theme) {
        //把已结束的会议排除
        QueryWrapper<Reservation> wrapper = new QueryWrapper<>();
        wrapper.eq("theme",theme);
        List<Reservation> reservations = this.list(wrapper);
        List<Reservation> reservationList = reservations.stream().filter(item -> (item.getStatus().equalsIgnoreCase(CommonConstant.RESERVATION_STATUS_1)) || (item.getStatus().equalsIgnoreCase(CommonConstant.RESERVATION_STATUS_2))).collect(Collectors.toList());
        //获取会议发起人
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(new QueryWrapper<AuthorParticipant>().eq("reservation_id", reservations.get(0).getId()));
        //将会议开始和结束时间做一下处理
        reservationList.stream().forEach(item ->{
            item.setAuthorPhone(authorParticipantList.get(0).getAuthorPhone());
            item.setBeginTime(TimeStringUtils.getBeginEndTime(item.getBeginTime()));
        });
        return reservationList;
    }

    /**
     * 小程序端预约记录数据展示
     * @param
     * @return
     */
    @Override
    public List<Reservation> getReservationRecordList(String authorPhone) {
        //把已结束的会议全部返回
        QueryWrapper<AuthorParticipant> wrapper = new QueryWrapper<>();
        wrapper.eq("author_phone",authorPhone).or()
                .eq("participant",authorPhone);
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(wrapper);
        if(CollectionUtils.isEmpty(authorParticipantList)){
            return null;
        }
        //获取会议id
        List<Integer> reservationId = authorParticipantList.stream().map(AuthorParticipant::getReservationId).collect(Collectors.toList());
        List<Reservation> reservations = this.listByIds(reservationId);
        List<Reservation> reservationList = reservations.stream().filter(item -> item.getStatus().equalsIgnoreCase(CommonConstant.RESERVATION_STATUS_3)).collect(Collectors.toList());
        //将会议开始和结束时间做一下处理
        reservationList.stream().forEach(item ->{
            item.setAuthorPhone(authorParticipantList.get(0).getAuthorPhone());
            item.setBeginTime(TimeStringUtils.getBeginEndTime(item.getBeginTime()));
        });
        return reservationList;
    }

    /**
     * 小程序端根据主题搜索预约记录会议
     * @param
     * @return
     */
    @Override
    public List<Reservation> getReservationRecord(String theme) {
        //把已结束的会议排除
        QueryWrapper<Reservation> wrapper = new QueryWrapper<>();
        wrapper.eq("theme",theme);
        List<Reservation> reservations = this.list(wrapper);
        List<Reservation> reservationList = reservations.stream().filter(item -> item.getStatus().equalsIgnoreCase(CommonConstant.RESERVATION_STATUS_3)).collect(Collectors.toList());
        //获取会议发起人
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(new QueryWrapper<AuthorParticipant>().eq("reservation_id", reservations.get(0).getId()));
        //将会议开始和结束时间做一下处理
        reservationList.stream().forEach(item ->{
            item.setAuthorPhone(authorParticipantList.get(0).getAuthorPhone());
            item.setBeginTime(TimeStringUtils.getBeginEndTime(item.getBeginTime()));
        });
        return reservationList;
    }

    /**
     * 会议详情页
     * @param
     * @return
     */
    @Override
    public Reservation getDetail(int id) {
        Reservation reservation = this.getById(id);
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(new QueryWrapper<AuthorParticipant>().eq("reservation_id", id));
        //将会议开始和结束时间做一下处理
        reservation.setAuthorPhone(authorParticipantList.get(0).getAuthorPhone());
        reservation.setBeginTime(TimeStringUtils.getBeginEndTime(reservation.getBeginTime()));
        return reservation;
    }

    /**
     * 取消会议
     * @param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeMeetById(int id) {
        Reservation reservation = this.getById(id);
        if(reservation.getStatus().equalsIgnoreCase(CommonConstant.RESERVATION_STATUS_2)){
            throw new ServiceException(ResponseEnum.MEET_BEGIN);
        }
        //TODO:取消会议后异步发送通知给用户：由于短信模板有限，暂时使用预约成功模板
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(new QueryWrapper<AuthorParticipant>().eq("reservation_id", reservation.getId()));
        if(!CollectionUtils.isEmpty(authorParticipantList)){
            List<String> participants = authorParticipantList.stream().map(AuthorParticipant::getParticipant).collect(Collectors.toList());
            String beginTime = TimeStringUtils.getBeginTime(reservation.getBeginTime());
            asyncService.sendCancelMessage(participants,authorParticipantList.get(0).getAuthorPhone(),beginTime);
        }
        //将所选会议室状态设置为未预约
        List<Room> roomList = roomMapper.selectList(new QueryWrapper<Room>().eq("name", reservation.getRoomName()));
        roomList.stream().forEach(room -> {
            room.setStatus(CommonConstant.ROOM_STATUS_NOT_RESERVATION);
            roomMapper.updateById(room);
        });
        //会议取消后将与会议有关的会议发起人和参会人数据删除
        authorParticipantService.remove(new QueryWrapper<AuthorParticipant>().eq("reservation_id",id));
        //同时把参会记录删除
        signService.remove(new QueryWrapper<Sign>().eq("reservation_id",id));
        this.removeById(id);
    }

    /**
     * 延长会议
     * @param
     * @return
     */
    @Override
    public void addLength(RsAddLengthVo rsAddLengthVo) {
        Reservation reservation = this.getById(rsAddLengthVo.getId());
        String beginTime = TimeStringUtils.getBeginTime(reservation.getBeginTime());
        String endTime = TimeStringUtils.getEndTime(reservation.getBeginTime());
        DateTime parsedTime = DateUtil.parse(endTime);
        DateTime dateTime = DateUtil.offsetMinute(parsedTime, rsAddLengthVo.getLength());
        String lengthEndTime = DateUtil.format(dateTime, "yyyy-MM-dd HH:mm");
        String newLengthBeginEndTime = TimeStringUtils.getDateTimeString(beginTime, lengthEndTime);
        reservation.setBeginTime(newLengthBeginEndTime);
        this.updateById(reservation);
        //TODO:异步去发送一条通知给参会人会议时长已延长：由于短信模板有限，暂时使用预约成功模板
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(new QueryWrapper<AuthorParticipant>().eq("reservation_id", reservation.getId()));
        if(!CollectionUtils.isEmpty(authorParticipantList)){
            List<String> participants = authorParticipantList.stream().map(AuthorParticipant::getParticipant).collect(Collectors.toList());
            asyncService.sendAddLengthMessage(participants,authorParticipantList.get(0).getAuthorPhone(),beginTime);
        }
    }

    /**
     * 会议详情页参会人数据
     * @param
     * @return
     */
    @Override
    public List<DeptUserVo> getDetailParticipants(int id) {
        //根据会议室Id获取对应的参会人数据
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(new QueryWrapper<AuthorParticipant>().eq("reservation_id", id));
        List<DeptUserVo> participants = new ArrayList<>();
        authorParticipantList.stream().forEach(item->{
            //根据参会人号码去查询参会人信息
            User user = userService.getOne(new QueryWrapper<User>().eq("phone", item.getParticipant()));
            //根据部门id获取部门
            Department department = departmentMapper.selectById(user.getDeptId());
            DeptUserVo deptUserVo = new DeptUserVo();
            deptUserVo.setDeptName(department.getName());
            deptUserVo.setPhone(user.getPhone());
            deptUserVo.setAvatar(user.getAvatar());
            deptUserVo.setName(user.getName());
            deptUserVo.setRole(user.getRole());
            participants.add(deptUserVo);
        });
        return participants;
    }

    @Override
    public Reservation getReservationById(Integer id) {
        Reservation reservation = this.getById(id);
        List<AuthorParticipant> authorParticipantList = authorParticipantService.list(new QueryWrapper<AuthorParticipant>().eq("reservation_id", id));
        if(CollectionUtils.isEmpty(authorParticipantList)){
            return null;
        }
        reservation.setAuthorPhone(authorParticipantList.get(0).getAuthorPhone());
        List<String> participants = new ArrayList<>();
        authorParticipantList.stream().forEach(item -> {
            participants.add(item.getParticipant());
        });
        reservation.setParticipants(participants);
        List<String> beginTimes = new ArrayList<>();
        String beginTime = TimeStringUtils.getBeginTime(reservation.getBeginTime());
        String endTime = TimeStringUtils.getEndTime(reservation.getBeginTime());
        beginTimes.add(beginTime);
        beginTimes.add(endTime);
        reservation.setBeginEndTime(beginTimes);
        return reservation;
    }
}
